home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
ARP.C
< prev
next >
Wrap
Text File
|
1993-09-19
|
11KB
|
375 lines
/* Address Resolution Protocol (ARP) functions. Sits between IP and
* Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
* mods for WAMPES by DB3FL 1990,1991
* using ARP_FILE_VERSION 3 from Oct 91 on. Now included the mode of the
* ax25 connection (stored in "->flags" - same value as defined in iface.h)
*/
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "iface.h"
#include "enet.h"
#include "ax25.h"
#include "icmp.h"
#include "ip.h"
#include "arp.h"
#include "files.h"
struct arp_tab *Arp_tab = NULLARP;
struct arp_stat Arp_stat;
/* Copy a host format arp structure into mbuf for transmission */
static struct mbuf * near
htonarp(struct arp *arp)
{
struct mbuf *bp = alloc_mbuf(ARPLEN + (2 * uchar(arp->hwalen)));
char *buf = bp->data;
buf = put16(buf,arp->hardware);
buf = put16(buf,arp->protocol);
*buf++ = arp->hwalen;
*buf++ = arp->pralen;
buf = put16(buf,arp->opcode);
memcpy(buf,arp->shwaddr,arp->hwalen);
buf += arp->hwalen;
buf = put32(buf,arp->sprotaddr);
memcpy(buf,arp->thwaddr,arp->hwalen);
buf += arp->hwalen;
buf = put32(buf,arp->tprotaddr);
bp->cnt = buf - bp->data;
return bp;
}
/* Send an ARP request to resolve IP address target_ip */
static void near
arp_output(struct iface *iface,int16 hardware,int32 target)
{
struct arp arp;
struct mbuf *bp;
struct arp_type *at = &Arp_type[hardware];
if(iface->output != NULLFP) {
arp.hardware = hardware;
arp.protocol = at->iptype;
arp.hwalen = at->hwalen;
arp.pralen = sizeof(int32);
arp.opcode = ARP_REQUEST;
memcpy(arp.shwaddr,iface->hwaddr,at->hwalen);
arp.sprotaddr = iface->addr;
memset(arp.thwaddr,0,at->hwalen);
arp.tprotaddr = target;
Arp_stat.outreq++;
bp = htonarp(&arp);
if(iface->forw != NULLIF) {
iface = iface->forw;
}
(*iface->output)(iface,at->bdcst,iface->hwaddr,at->arptype,bp);
}
}
/* Resolve an IP address to a hardware address; if not found,
* initiate query and return NULLCHAR. If an address is returned, the
* interface driver may send the packet; if NULLCHAR is returned,
* res_arp() will have saved the packet on its pending queue,
* so no further action (like freeing the packet) is necessary.
*/
char *
res_arp(
struct iface *iface, /* Pointer to interface block */
int16 hardware, /* Hardware type */
int32 target, /* Target IP address */
struct mbuf *bp) /* IP datagram to be queued if unresolved */
{
struct arp_tab *arp;
if((arp = arp_lookup(hardware,target)) != NULLARP) {
struct ip ip;
if(arp->state == ARP_VALID)
return arp->hw_addr;
/* Earlier packets are already pending, kick this one back
* as a source quench
*/
ntohip(&ip,&bp);
icmp_output(&ip,bp,ICMP_QUENCH,0,NULL);
free_p(bp);
} else {
/* Create an entry and put the datagram on the
* queue pending an answer
*/
arp = arp_add(target,hardware,NULLCHAR,0,0);
enqueue(&arp->pending,bp);
arp_output(iface,hardware,target);
}
return NULLCHAR;
}
/* Handle incoming ARP packets. This is almost a direct implementation of
* the algorithm on page 5 of RFC 826, except for:
* 1. Outgoing datagrams to unresolved addresses are kept on a queue
* pending a reply to our ARP request.
* 2. The names of the fields in the ARP packet were made more mnemonic.
* 3. Requests for IP addresses listed in our table as "published" are
* responded to, even if the address is not our own.
*/
void
arp_input(struct iface *iface,struct mbuf *bp)
{
struct arp arp;
struct arp_tab *ap;
struct arp_type *at;
Arp_stat.recv++;
if(ntoharp(&arp,&bp) == -1) /* Convert into host format */
return;
if(arp.hardware >= NHWTYPES){
/* Unknown hardware type, ignore */
Arp_stat.badtype++;
return;
}
at = &Arp_type[arp.hardware];
if(arp.protocol != at->iptype){
/* Unsupported protocol type, ignore */
Arp_stat.badtype++;
return;
}
if((unsigned)(arp.hwalen) > MAXHWALEN || uchar(arp.pralen) != sizeof(int32)){
/* Incorrect protocol addr length (different hw addr lengths
* are OK since AX.25 addresses can be of variable length)
*/
Arp_stat.badlen++;
return;
}
if(arp.sprotaddr == 0L || arp.tprotaddr == 0L){
/* We are going to dead-end references for [0.0.0.0], since
* experience shows that these cause total lock up -- N1BEE
*/
Arp_stat.badaddr++;
return;
}
if(memcmp(arp.shwaddr,at->bdcst,at->hwalen) == 0){
/* This guy is trying to say he's got the broadcast address! */
Arp_stat.badaddr++;
return;
}
/* If this guy is already in the table, update its entry
* unless it's a manual entry (noted by the lack of a timer)
*/
ap = NULLARP; /* ap plays the role of merge_flag in the spec */
if((ap = arp_lookup(arp.hardware,arp.sprotaddr)) != NULLARP
&& dur_timer(&ap->timer) != 0){
ap = arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,0,0);
}
/* See if we're the address they're looking for */
if(ismyaddr(arp.tprotaddr) != NULLIF){
if(ap == NULLARP) /* Only if not already in the table */
arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,0,0);
if(arp.opcode == ARP_REQUEST){
/* Swap sender's and target's (us) hardware and protocol
* fields, and send the packet back as a reply
*/
memcpy(arp.thwaddr,arp.shwaddr,arp.hwalen);
/* Mark the end of the sender's AX.25 address
* in case he didn't
*/
if(arp.hardware == ARP_AX25)
arp.thwaddr[uchar(arp.hwalen)-1] |= E;
memcpy(arp.shwaddr,iface->hwaddr,at->hwalen);
arp.tprotaddr = arp.sprotaddr;
arp.sprotaddr = iface->addr;
arp.opcode = ARP_REPLY;
bp = htonarp(&arp);
if(iface->forw != NULLIF)
iface = iface->forw;
(*iface->output)(iface,arp.thwaddr,iface->hwaddr,at->arptype,bp);
Arp_stat.inreq++;
} else {
Arp_stat.replies++;
}
} else if(arp.opcode == ARP_REQUEST
&& (ap = arp_lookup(arp.hardware,arp.tprotaddr)) != NULLARP
&& ap->pub){
/* Otherwise, respond if the guy he's looking for is
* published in our table.
*/
memcpy(arp.thwaddr,arp.shwaddr,arp.hwalen);
/* Mark the end of the sender's AX.25 address
* in case he didn't
*/
if(arp.hardware == ARP_AX25)
arp.thwaddr[ALEN] |= E;
memcpy(arp.shwaddr,ap->hw_addr,at->hwalen);
arp.tprotaddr = arp.sprotaddr;
arp.sprotaddr = ap->ip_addr;
arp.opcode = ARP_REPLY;
bp = htonarp(&arp);
if(iface->forw != NULLIF)
iface = iface->forw;
(*iface->output)(iface,arp.thwaddr,iface->hwaddr,at->arptype,bp);
Arp_stat.inreq++;
} else if(arp.opcode == RARP_REQUEST) {
for(ap = Arp_tab; ap != NULLARP; ap = ap->next)
if(memcmp(ap->hw_addr,arp.thwaddr,at->hwalen) == 0)
goto found;
found:
if(ap != NULLARP && ap->pub) {
memcpy(arp.shwaddr,iface->hwaddr,at->hwalen);
arp.tprotaddr = ap->ip_addr;
arp.sprotaddr = iface->addr;
arp.opcode = RARP_REPLY;
bp = htonarp(&arp);
if(iface->forw != NULLIF)
iface = iface->forw;
(*iface->output)(iface,arp.thwaddr,iface->hwaddr,REVARP_TYPE,bp);
Arp_stat.inreq++;
}
}
}
/* Add an IP-addr / hardware-addr pair to the ARP table */
struct arp_tab *
arp_add(
int32 ipaddr, /* IP address, host order */
int16 hardware, /* Hardware type */
char *hw_addr, /* Hardware address, if known; NULLCHAR otherwise */
int pub, /* Publish this entry? */
int flags) /* ax25 connection mode */
{
struct arp_tab *ap;
struct arp_type *at = &Arp_type[hardware];
if(hardware >= NHWTYPES) {
return NULLARP; /* Invalid hardware type */
}
if((ap = arp_lookup(hardware,ipaddr)) == NULLARP) {
/* New entry */
ap = mxallocw(sizeof(struct arp_tab));
ap->hw_addr = mxallocw(at->hwalen);
ap->timer.func = arp_drop;
ap->timer.arg = ap;
ap->hardware = hardware;
ap->ip_addr = ipaddr;
ap->hwalen = at->hwalen;
ap->next = Arp_tab;
Arp_tab = ap;
}
ap->flags = (ap->hardware == ARP_AX25) ? flags : 0;
if(hw_addr == NULLCHAR){
/* Await response */
ap->state = ARP_PENDING;
set_timer(&ap->timer,Arp_type[hardware].pendtime * 1000L);
} else {
struct mbuf *bp;
/* Response has come in, update entry and run through queue */
ap->state = ARP_VALID;
set_timer(&ap->timer,ARPLIFE * 1000L);
xfree(ap->hw_addr);
ap->hw_addr = mxallocw(at->hwalen);
memcpy(ap->hw_addr,hw_addr,at->hwalen);
/* This kludge marks the end of an AX.25 address to allow
* for optional digipeaters (insert Joan Rivers salute here)
*
* changed back to permanent length for use with WNOS routing
* DB3FL - 1991
*/
if(hardware == ARP_AX25) {
ap->hw_addr[ALEN] |= E;
}
if(pub) { /* leave published entries as published */
ap->pub = 1;
}
while((bp = dequeue(&ap->pending)) != NULLBUF) {
ip_route(NULLIF,NULLIF,bp,0);
}
}
start_timer(&ap->timer);
return ap;
}
/* Remove an entry from the ARP table */
void
arp_drop(void *p)
{
struct arp_tab *ap = p, *app, *aplast = NULLARP;
for(app = Arp_tab; app != NULLARP; aplast = app, app = app->next) {
if(app == ap)
break;
}
if(app == NULLARP)
return;
if(aplast != NULLARP)
aplast->next = app->next;
else
Arp_tab = app->next;
/* Timer should already be stopped, but just in case... */
stop_timer(&app->timer);
free_q(&app->pending);
xfree(app->hw_addr);
xfree(app);
}
/* Look up the given IP address in the ARP table */
struct arp_tab *
arp_lookup(int16 hardware,int32 ipaddr)
{
struct arp_tab *ap, *aplast = NULLARP;
for(ap = Arp_tab; ap != NULLARP; aplast = ap, ap = ap->next) {
if(ap->ip_addr == ipaddr && ap->hardware == hardware) {
if(aplast != NULLARP) {
/* Move to top of list */
aplast->next = ap->next;
ap->next = Arp_tab;
Arp_tab = ap;
}
return ap;
}
}
return NULLARP;
}
/* Convert an incoming ARP packet into a host-format structure */
int
ntoharp(struct arp *arp,struct mbuf **bpp)
{
if(arp == NULL || bpp == NULLBUFP) {
return -1;
}
arp->hardware = pull16(bpp);
arp->protocol = pull16(bpp);
arp->hwalen = PULLCHAR(bpp);
arp->pralen = PULLCHAR(bpp);
arp->opcode = pull16(bpp);
pullup(bpp,arp->shwaddr,(int16)uchar(arp->hwalen));
arp->sprotaddr = pull32(bpp);
pullup(bpp,arp->thwaddr,(int16)uchar(arp->hwalen));
arp->tprotaddr = pull32(bpp);
/* Get rid of anything left over */
free_p(*bpp);
*bpp = NULLBUF;
return 0;
}